﻿using System.Collections.Generic;
using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Text;
using System.Threading;
using GPC_Overlay;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure.Diagnostics;
using System.IO;

namespace GISWorkerRole
{
    public class WorkerRole : RoleEntryPoint
    {
        string connectionString;
        string instance_id;
        string  op;
        public static int concurrencyLimit = 8;
        public static int currentOps = 0;
        public static int processed = 0;
        public static int toprocess = 0;
        public static List<string> blobs;
        public static List<Polygon> basePoly;
        public static List<Polygon> overlayPoly;

        public int numMessages = -1;

        TimeSpan waitingTime = new TimeSpan(0, 0, 0);
        TimeSpan parseTime = new TimeSpan(0, 0, 0);
        TimeSpan taskCreation = new TimeSpan(0,0,0);
        TimeSpan processingTime = new TimeSpan(0, 0, 0);
        TimeSpan writingTime = new TimeSpan(0, 0, 0);

        TimeSpan cloudReadTime = new TimeSpan(0, 0, 0);
        TimeSpan cloudWriteTime = new TimeSpan(0, 0, 0);
        TimeSpan totalProcessingTime = new TimeSpan(0, 0, 0);
        TimeSpan cloudQueuingTime = new TimeSpan(0, 0, 0);

        public override void Run()
        {
            connectionString = RoleEnvironment.GetConfigurationSettingValue("DataConnectionString");
            QueueHelper QueueObject = new QueueHelper(connectionString);
            CloudQueueMessage msg = new CloudQueueMessage("");

            string instanceId = RoleEnvironment.CurrentRoleInstance.Id;
            op = String.Empty;
            string inputMsg = String.Empty;
            int count = 0;
            while (count == 0)
            {
                QueueObject.GetApproxMessageCount(DataConstants.INPUT_PARAS, out count);
                if (count == 0)
                    Thread.Sleep(5000);
            }

            QueueObject.PeekMessage(DataConstants.INPUT_PARAS, out msg);
            inputMsg = msg.AsString;
            
            string[] splitMsg = inputMsg.Split('{');
            op = splitMsg[2];
          
            while (true)
            {
                if (QueueObject.GetMessage(DataConstants.WORKER_QUEUE, out msg))
                {
                    _handleBlob(msg.AsString, DateTime.Now);
                    QueueObject.DeleteMessage(DataConstants.WORKER_QUEUE, msg);
                }
                else
                {
                    Thread.Sleep(5000);
                }
            }
            
        }

        private void _handleMessage(CloudQueueMessage message,QueueHelper queueObject, DateTime timeToRead, int queueID)
        {
            BlobHelper blobObject = new BlobHelper(connectionString);
            string polygonMsg = "";

            /* The message has the blob Id. Read it and get the blob*/
            string id = message.AsString;
            try
            {
                blobObject.GetBlob(DataConstants.MAP_CONTAINER_BLOB, id, out polygonMsg);
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine(e.Message + " :Error during GetBlob.");
            }

            /* The blob contains the GMl. We need to convert it to Polygon Object */
            Polygon blob = new Polygon();
            try
            {
                blob = Parser.PolygonFromString(polygonMsg);
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine(e.Message + " :Error during Polygon From String - worker role. PolygonMsg = " + polygonMsg);
            }

            /* Since we have the task as an object. Let us process it. Yippee! */
            try
            {
                TimeSpan ReadTime = DateTime.Now.Subtract(timeToRead);
                _processPolygon(blob, blobObject, id, ReadTime.ToString());
       
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine(e.Message + " :Error during process Polygon.");
            }

            /* If everything went fine, God forbid, let us get rid of the corpse */
            try
            {
                blobObject.DeleteBlob(DataConstants.MAP_CONTAINER_BLOB, id);
                QueueHelper.DeleteMessageStatic(connectionString, DataConstants.WORKER_QUEUE, message);
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine(e.Message + " :Error during residue deletion.");
            }
        }

        public override bool OnStart()
        {
            // Set the maximum number of concurrent connections 
            ServicePointManager.DefaultConnectionLimit = 48;

            this.instance_id = RoleEnvironment.CurrentRoleInstance.Id.ToString();
            
            // For information ons handling configuration changes
            // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                // Provide the configSetter with the initial value
                configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));

                RoleEnvironment.Changed += (sender, arg) =>
                {
                    foreach (var change in arg.Changes)
                    {
                        var configChange = change as RoleEnvironmentConfigurationSettingChange;
                        if (configChange != null)
                        {
                            RoleEnvironment.GetConfigurationSettingValue(
                                configChange.ConfigurationSettingName);
                        }
                    }
                };
            });


            var diag = DiagnosticMonitor.GetDefaultInitialConfiguration();
            diag.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
            diag.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
            diag.WindowsEventLog.DataSources.Add("Application!*");
            diag.WindowsEventLog.DataSources.Add("System!*");
            diag.WindowsEventLog.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
            diag.DiagnosticInfrastructureLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
            DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", diag);

            return base.OnStart();
        }

        private bool _handleBlob(string blobid, DateTime timeToRead)
        {
            BlobHelper blobObject = new BlobHelper(connectionString);
            string polygonMsg = "";
            
            try
            {
                blobObject.GetBlob(DataConstants.OUTPUT_CONTAINER_BLOB, blobid, out polygonMsg);
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine(e.Message + " :Error during GetBlob.");
                return false;
            }
            
            cloudReadTime = cloudReadTime.Add(DateTime.Now.Subtract(timeToRead));

            DateTime processTime = DateTime.Now;
            /* The blob contains the GMl. We need to convert it to Polygon Object */
            Polygon blob = new Polygon();
            try
            {
                blob = Parser.PolygonFromString(polygonMsg);
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine(e.Message + " :Error during Polygon From String - worker role. PolygonMsg = " + polygonMsg);
                return false;
            }
            totalProcessingTime = totalProcessingTime.Add(DateTime.Now.Subtract(processTime));

            /* Since we have the task let us process it. Yippee! */
            try
            {
                TimeSpan ReadTime = DateTime.Now.Subtract(timeToRead);
                _processPolygon(blob, blobObject, blobid, ReadTime.ToString());
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine(e.Message + " :Error during process Polygon.");
                return false;
            }

            /* If everything went fine, God forbid, let us get rid of the corpse */
            try
            {
                blobObject.DeleteBlob(DataConstants.OUTPUT_CONTAINER_BLOB, blobid);
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine(e.Message + " :Error during residue deletion.");
                return false;
            }
            return true;
        }

        private void _processPolygon(Polygon polygon, BlobHelper blobObject, string blobID, string ReadTime)
        {
            DateTime startProc = DateTime.Now;
            DateTime startSave = DateTime.Now;
            QueueHelper queueObject = new QueueHelper(connectionString);
            Polygon rslt = new Polygon();
            StringBuilder resultString = new StringBuilder();
            TimeSpan procTime = new TimeSpan(0, 0, 0, 0, 0);
            TimeSpan writeTime = new TimeSpan(0, 0, 0, 0, 0);
            int count = 0;

            try
            {
                /* find how many clipping operations are to be done and do them in a loop */
                count = polygon.overlayPolygons.Count;
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine("Error in counting overlay polygon" + e.Message);
            }
            /* Obviously this can be parallelized but we only use a single core worker */

   
            for (int r = 0; r < count; r++)
            {
                /* Release the kraken and let it take care of polygons */
                try
                {
                    rslt = polygon.Clip((GpcOperation)Enum.Parse(typeof(GpcOperation), op), polygon.overlayPolygons[r]);
                    /* Clipper library doesn't store params */
                    rslt.fID = polygon.fID + "-" + polygon.overlayPolygons[r].fID;
                    rslt.srsname = polygon.srsname;
                    rslt.srsDimension = polygon.srsDimension;
                }
                catch (Exception e)
                {
                    System.Diagnostics.Trace.WriteLine(e.Message + " :Error during GPC Clipping.");
                }

                /* The clipping library returned the resultant polygon. Let us save it to our favorite GML format. */
                procTime = procTime.Add(DateTime.Now.Subtract(startProc));
                
                try
                {
                    startSave = DateTime.Now;
                    resultString.Append(Parser.PolygonToString(rslt)); /* <-- This line is the main kicker. */
                    writeTime = writeTime.Add(DateTime.Now.Subtract(startSave));
                }
                catch (Exception e)
                {
                    System.Diagnostics.Trace.WriteLine(e.Message + " Error after clipping" + Parser.PolygonToString(rslt));
                }
            }
            totalProcessingTime = totalProcessingTime.Add(DateTime.Now.Subtract(startProc));
            try
            {
                string blockid = Guid.NewGuid().ToString();
                if (resultString.ToString().Length > 0)
                {
                    startSave = DateTime.Now;
                    /* So private, you have the resultant GML. You are on cloud 9, can your result not be on cloud? Put me there sir!*/
                    BlobHelper.PutBlock(connectionString, DataConstants.MAP_CONTAINER_BLOB, DataConstants.OUTPUT_BLOB, blockid, resultString.ToString());

                    writeTime = writeTime.Add(DateTime.Now.Subtract(startSave));
                    cloudWriteTime = cloudWriteTime.Add(DateTime.Now.Subtract(startSave));
                }
                /* Web role is getting paid without doing anything. Let us store a message so that web role can keep track of how many messages we actually processed. 
                    * This is for book keeping, needed for reporting graphs in reports. It is not doing anything good to the process. However, if you need a notification of things ending you need this.*/
                queueObject.PutMessage(DataConstants.RESULT_QUEUE, new CloudQueueMessage(DateTime.Now.ToString()));
                //queueObject.PutMessage(DataConstants.RESULT_QUEUE, new CloudQueueMessage(this.instance_id + "{" + DateTime.Now.ToString() + "{" + "{" + ReadTime.ToString() + "{" + procTime.ToString() + "{" + writeTime.ToString()));
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine(e.Message + " :Error during put block/message - " + e.InnerException);
            }

        }
    }
}
